package cn.kkcxy.blog.system.service.impl;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;
import cn.kkcxy.blog.common.utils.IpUtil;
import cn.kkcxy.blog.common.utils.RegionUtil;
import cn.kkcxy.blog.core.entity.sys.SysVisitorLog;
import cn.kkcxy.blog.core.enums.ArticleCategoryEnum;
import cn.kkcxy.blog.core.enums.DeletedEnum;
import cn.kkcxy.blog.core.vo.client.ClientProfileVO;
import cn.kkcxy.blog.core.vo.client.archive.category.ClientCommonArticleCategoryVO;
import cn.kkcxy.blog.core.vo.client.archive.tag.ClientArticleTagsInfoVO;
import cn.kkcxy.blog.system.service.PortalService;
import cn.kkcxy.blog.system.service.article.ArticleCategoryService;
import cn.kkcxy.blog.system.service.article.ArticleService;
import cn.kkcxy.blog.system.service.article.ArticleTagService;
import cn.kkcxy.blog.system.service.sys.SysVisitorLogService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @BelongsProject: kkcxy-blog
 * @BelongsPackage: cn.kkcxy.blog.system.service.impl
 * @Author: kkcxy
 * @CreateTime: 2024-09-13 02:38
 * @Description: User/Portal Service Impl
 * @Version: 1.0
 */
@Slf4j
@Service
public class PortalServiceImpl implements PortalService {

    /**
     * Article Service
     */
    @Resource
    private ArticleService articleService;

    /**
     * Article Category Service
     */
    @Resource
    private ArticleCategoryService articleCategoryService;

    /**
     * Article Tag Service
     */
    @Resource
    private ArticleTagService articleTagService;

    /**
     * SysVisitorLog Service
     */
    @Resource
    private SysVisitorLogService sysVisitorLogService;

    /**
     * Common ThreadPool
     */
    @Resource
    private ThreadPoolExecutor commonThreadPool;

    /**
     * @description: [用户端] 获取文章分类数据
     * @author: kkcxy
     * @date: 2024/9/26 22:15
     * @param: []
     * @return: java.util.List<cn.kkcxy.blog.core.vo.client.archive.category.ClientSecondLevelArticleCategoryVO>
     **/
    @Override
    public List<ClientCommonArticleCategoryVO> getArticleCategories() {
        // 构建结果集
        List<ClientCommonArticleCategoryVO> vo = new ArrayList<>();
        // 得到所有一级分类
        List<ClientCommonArticleCategoryVO> primaryCategories = articleCategoryService.getArticleCategories(0L, ArticleCategoryEnum.PRIMARY.getType(), DeletedEnum.NO.getStatus());
        // 一级分类为空 直接返回空集合
        if (!CollectionUtils.isEmpty(primaryCategories)) {
            // 遍历一级分类
            for (ClientCommonArticleCategoryVO primaryCategory : primaryCategories) {
                // 一级分类id
                Long primaryCategoryId = primaryCategory.getId();
                // 二级分类ids
                List<Long> categoryIds = articleCategoryService.getSecondLevelCategoryIdsByPrimaryCategoryId(primaryCategoryId);
                // 将一级分类和二级分类id整合
                categoryIds.add(primaryCategoryId);
                // 获取文章分类下文章数
                List<ClientCommonArticleCategoryVO> categoryCounts = articleCategoryService.getArticleCategoryCountListByCategoryIds(categoryIds);
                // 设置一级分类文章总数
                primaryCategory.setCount(categoryCounts.stream().mapToInt(ClientCommonArticleCategoryVO::getCount).sum());
                // 将一级分类添加进vo中
                vo.add(primaryCategory);
                // 将不是一级分类的分类直接添加进vo中
                for (ClientCommonArticleCategoryVO categoryCount : categoryCounts) {
                    if (!Objects.equals(primaryCategoryId, categoryCount.getId())) {
                        vo.add(categoryCount);
                    }
                }
            }
        }
        return vo;
    }

    /**
     * @description: [用户端] 获取文章标签数据
     * @author: kkcxy
     * @date: 2024/9/26 22:15
     * @param: []
     * @return: java.util.List<cn.kkcxy.blog.core.vo.client.archive.tag.ClientArticleTagsInfoVO>
     **/
    @Override
    public List<ClientArticleTagsInfoVO> getArticleTags() {
        return articleTagService.getArticleTags();
    }

    /**
     * @description: [用户端] 获取网站详情数据
     * @author: kkcxy
     * @date: 2024/9/28 22:44
     * @param: []
     * @return: cn.kkcxy.blog.core.vo.client.ClientProfileVO
     **/
    @Override
    @SneakyThrows
    public ClientProfileVO getProfile() {
        // 1.获得request 获取id region
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = IpUtil.getIpAddr(request);
        String region = RegionUtil.getIpRegion(ip);

        // 2.1执行获得文章总数
        CompletableFuture<Integer> articleCountFuture = CompletableFuture.supplyAsync(() -> articleService.getArticleCount(), commonThreadPool);

        // 2.2执行获得分类总数
        CompletableFuture<Integer> articleCategoryCountFuture = CompletableFuture.supplyAsync(() -> articleCategoryService.getCategoryCount(), commonThreadPool);

        // 2.3执行获得标签总数
        CompletableFuture<Integer> articleTagCountFuture = CompletableFuture.supplyAsync(() -> articleTagService.getTagCount(), commonThreadPool);

        // 2.4执行获得访客总数
        CompletableFuture<Integer> visitorCountFuture = CompletableFuture.supplyAsync(() -> sysVisitorLogService.getVisitorCount(), commonThreadPool);

        // 2.5等待所有线程执行完毕
        CompletableFuture.allOf(articleCountFuture, articleCategoryCountFuture, articleTagCountFuture, visitorCountFuture).join();

        // 3. 记录访客信息
        String role = "";
        Long userId = null;
        try {
            userId = Long.parseLong((String) StpUtil.getLoginId());
            role = "平台用户";
        } catch (NotLoginException e) {
            role = "游客用户";
        }
        saveVisitorLog(userId, role, ip, region);
        // 返回结果
        return new ClientProfileVO(articleCountFuture.get(), articleCategoryCountFuture.get(), articleTagCountFuture.get(), visitorCountFuture.get());
    }

    /**
     * @description: 记录访客信息
     * @author: kkcxy
     * @date: 2024/9/29 2:17
     * @param: [userId, role, ip, region]
     * @return: void
     **/
    @Async("asyncServerExecutor")
    public void saveVisitorLog(Long userId, String role, String ip, String region) {
        SysVisitorLog sysVisitorLog = new SysVisitorLog();
        sysVisitorLog.setUserId(userId);
        sysVisitorLog.setRole(role);
        sysVisitorLog.setIp(ip);
        sysVisitorLog.setRegion(region);
        sysVisitorLog.setDeleted(DeletedEnum.NO.getStatus());
        sysVisitorLogService.save(sysVisitorLog);
    }
}
